/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
module dwt.widgets.filedialog;


private import dwt.dwt;

public import dwt.widgets.dialog;
private import dwt.widgets.display;
private import dwt.widgets.shell;
private import dwt.internal.win32.os;

private import dwt.util.util;



/**
 * Instances of this class allow the user to navigate
 * the file system and select or enter a file name.
 * <dl>
 * <dt><b>Styles:</b></dt>
 * <dd>SAVE, OPEN, MULTI</dd>
 * <dt><b>Events:</b></dt>
 * <dd>(none)</dd>
 * </dl>
 * <p>
 * IMPORTANT: This class is intended to be subclassed <em>only</em>
 * within the DWT implementation.
 * </p>
 */
public class FileDialog : Dialog {

	char[][] filterNames;
	char[][] filterExtensions;
	char[][] fileNames;
	char[] filterPath, fileName;
	static const char[] FILTER = "*.*";
	static int BUFFER_SIZE = 1024 * 32;

/**
 * Constructs a new instance of this class given only its parent.
 *
 * @param parent a shell which will be the parent of the new instance
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
 *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
 * </ul>
 */
public this (Shell parent) {
	this (parent, DWT.PRIMARY_MODAL);
}

/**
 * Constructs a new instance of this class given its parent
 * and a style value describing its behavior and appearance.
 * <p>
 * The style value is either one of the style constants defined in
 * class <code>DWT</code> which is applicable to instances of this
 * class, or must be built by <em>bitwise OR</em>'ing together 
 * (that is, using the <code>int</code> "|" operator) two or more
 * of those <code>DWT</code> style constants. The class description
 * lists the style constants that are applicable to the class.
 * Style bits are also inherited from superclasses.
 * </p>
 *
 * @param parent a shell which will be the parent of the new instance
 * @param style the style of dialog to construct
 *
 * @exception IllegalArgumentException <ul>
 *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
 * </ul>
 * @exception SWTException <ul>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
 *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
 * </ul>
 */
public this (Shell parent, int style) {
	super (parent, style);
	checkSubclass ();
}

// added by <Shawn Liu>, to support center window to parent
extern (Windows)
static uint OFNHookProc (HWND hdlg, uint msg, uint wParam, int lParam) {
	int imsg = cast(int)msg;
	switch (imsg) {
		case OS.WM_INITDIALOG:
			OPENFILENAME* lpcf = cast(OPENFILENAME*)lParam;
			FileDialog pThis = cast(FileDialog)cast(void*)lpcf.lCustData;
			// <Shawn Liu> center to parent
			OS.CenterWindow(hdlg, pThis.parent.getSafeHwnd());
			break;
		default : break;
	}
	return 0;
}

/**
 * Returns the path of the first file that was
 * selected in the dialog relative to the filter path
 * 
 * @return the relative path of the file
 */
public char[] getFileName () {
	return fileName;
}

/**
 * Returns the paths of all files that were selected
 * in the dialog relative to the filter path.
 * 
 * @return the relative paths of the files
 */
public char[][] getFileNames () {
	return fileNames;
}

/**
 * Returns the file extensions which the dialog will
 * use to filter the files it shows.
 *
 * @return the file extensions filter
 */
public char[][] getFilterExtensions () {
	return filterExtensions;
}

/**
 * Returns the file names which the dialog will
 * use to filter the files it shows.
 *
 * @return the file name filter
 */
public char[][] getFilterNames () {
	return filterNames;
}

/**
 * Returns the directory path that the dialog will use.
 * File names in this path will appear in the dialog,
 * filtered according to the filter extensions.
 *
 * @return the directory path string
 * 
 * @see #setFilterExtensions
 */
public char[] getFilterPath () {
	return filterPath;
}

/**
 * Makes the dialog visible and brings it to the front
 * of the display.
 *
 * @return a string describing the absolute path of the first selected file,
 *         or null if the dialog was cancelled or an error occurred
 *
 * @exception SWTException <ul>
 *    <li>ERROR_WIDGET_DISPOSED - if the dialog has been disposed</li>
 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the dialog</li>
 * </ul>
 */
public char[] open () {
	
	/* Get the owner int for the dialog */
	HWND hwndOwner = null;
	if (parent !is null) hwndOwner = parent.handle;

	/* Convert the title and copy it into lpstrTitle */
	if (title is null) title = "";	

	/* Compute filters and copy into lpstrFilter */
	char[] strFilter = "";
//	if (filterNames is null) filterNames = new String [0];
//	if (filterExtensions is null) filterExtensions = new String [0];
	for (int i=0; i<filterExtensions.length; i++) {
		char[] filterName = filterExtensions [i];
		if (i < filterNames.length) filterName = filterNames [i];
		strFilter = strFilter ~ filterName ~'\0' ~ filterExtensions [i] ~ '\0';
	}
	
	if (filterExtensions.length == 0) {
		strFilter = strFilter ~ FILTER ~ '\0' ~ FILTER ~ '\0';
	}
	
	/* Convert the fileName and filterName to C strings */
	if (fileName is null) fileName = "";
	char[] name = fileName;

	/*
	* Copy the name into lpstrFile and ensure that the
	* last byte is NULL and the buffer does not overrun.
	*/
	int nMaxFile = OS.MAX_PATH;
	if ((style & DWT.MULTI) != 0) nMaxFile = Math.max (nMaxFile, BUFFER_SIZE);
	nMaxFile = Math.max(nMaxFile, cast(int)name.length + 1);
	TCHAR[] temp = Converter.StrToTCHARs(name);
	TCHAR[] lpstrFile = new TCHAR[nMaxFile];
	lpstrFile[0..temp.length] = temp[];
	lpstrFile[temp.length] = 0;

	/*
	* Copy the path into lpstrInitialDir and ensure that
	* the last byte is NULL and the buffer does not overrun.
	*/
	if (filterPath is null) filterPath = "";
	/* Use the character encoding for the default locale */
	char[] strInitialDir = Converter.replace(filterPath, "/", "\\");

	/* Create the file dialog strct */
	OPENFILENAME  ofn;
	OPENFILENAME* strct = &ofn;
	strct.lStructSize = OPENFILENAME.sizeof;
	strct.Flags = OS.OFN_HIDEREADONLY | OS.OFN_NOCHANGEDIR;// | OS.OFN_ENABLEHOOK;
	strct.lpfnHook = &OFNHookProc;
	strct.lCustData = cast(int)cast(void*)this;
	
	if ((style & DWT.MULTI) != 0) {
		strct.Flags |= OS.OFN_ALLOWMULTISELECT | OS.OFN_EXPLORER;
	}
	strct.hwndOwner = hwndOwner;
	strct.lpstrTitle = Converter.StrToTCHARz(title);
	strct.lpstrFile = cast(TCHAR*)lpstrFile;
	strct.nMaxFile = nMaxFile;
	strct.lpstrInitialDir = Converter.StrToTCHARz(strInitialDir);
	strct.lpstrFilter = Converter.StrToTCHARz(strFilter);
	strct.nFilterIndex = 0;

	/*
	* Set the default extension to an empty string.  If the
	* user fails to type an extension and this extension is
	* empty, Windows uses the current value of the filter
	* extension at the time that the dialog is closed.
	*/
	TCHAR lpstrDefExt = '\0';
	boolean save = (style & DWT.SAVE) != 0;
	if (save) {
		strct.lpstrDefExt = &lpstrDefExt;
	}
	
	/* Make the parent shell be temporary modal */
	Shell oldModal = null;
	Display display = null;
	if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) != 0) {
		display = parent.getDisplay ();
		oldModal = display.getModalDialogShell ();
		display.setModalDialogShell (parent);
	}
	
	/*
	* Open the dialog.  If the open fails due to an invalid
	* file name, use an empty file name and open it again.
	*/
	boolean success = (save) ? OS.GetSaveFileName (strct) : OS.GetOpenFileName (strct);
	if (OS.CommDlgExtendedError () == OS.FNERR_INVALIDFILENAME) {
		lpstrFile[0] = '\0';
		success = (save) ? OS.GetSaveFileName (strct) : OS.GetOpenFileName (strct);
	}

	/* Clear the temporary dialog modal parent */
	if ((style & (DWT.APPLICATION_MODAL | DWT.SYSTEM_MODAL)) != 0) {
		display.setModalDialogShell (oldModal);
	}
	
	/* Set the new path, file name and filter */
//	fileNames = new String [0];
	char[] fullPath = null;
	if (success) {
		
		/* Use the character encoding for the default locale */
		TCHAR[] buffer = lpstrFile;
		
		/*
		* Bug in WinCE.  For some reason, nFileOffset and nFileExtension
		* are always zero on WinCE HPC. nFileOffset is always zero on
		* WinCE PPC when using GetSaveFileName.  nFileOffset is correctly
		* set on WinCE PPC when using OpenFileName.  The fix is to parse
		* lpstrFile to calculate nFileOffset.
		* 
		* Note: WinCE does not support multi-select file dialogs.
		*/
		int nFileOffset = strct.nFileOffset;
		version(WinCE) 
		{
			if( nFileOffset == 0) {
				int index = 0; 
				while (index < buffer.length) {
					int ch = buffer[index];
					if (ch == 0) break;
					if (ch == '\\') nFileOffset = index + 1;
					index++;
				}
			}
		}
		if (nFileOffset > 0) {

			// get file directory
			filterPath = Converter.TCHARzToStr(buffer.ptr, nFileOffset - 1);
			
			/*
			* Get each file from the buffer.  Files are delimited
			* by a NULL character with 2 NULL characters at the end.
			*/
			int count = 0;
			int start = nFileOffset;
			do {
				int end = start;
				while (end < buffer.length && buffer[end] != 0) end++;
				wchar[] text = buffer[start..end];
				char[] string = Converter.TCHARzToStr(text.ptr, end - start);
				start = end;

				fileNames ~= string;
				if ((style & DWT.MULTI) == 0) break;
				start++;
			} while (start < buffer.length && buffer[start] != 0);
			
			if (fileNames.length > 0) fileName = fileNames  [0];
			char[] separator = "";
			int len = filterPath.length;
			if (len > 0 && filterPath[len - 1] != '\\') {
				separator = "\\";
			}
			fullPath = filterPath ~ separator ~ fileName;
		}
	}
	
	/* Free the memory that was allocated. */
//	OS.HeapFree (hHeap, 0, lpstrFile);

	/*
	* This code is intentionally commented.  On some
	* platforms, the owner window is repainted right
	* away when a dialog window exits.  This behavior
	* is currently unspecified.
	*/
//	if (hwndOwner != 0) OS.UpdateWindow (hwndOwner);
	
	/* Answer the full path or null */
	return fullPath;
}

/**
 * Set the initial filename which the dialog will
 * select by default when opened to the argument,
 * which may be null.  The name will be prefixed with
 * the filter path when one is supplied.
 * 
 * @param string the file name
 */
public void setFileName (char[] string) {
	fileName = string;
}

/**
 * Set the file extensions which the dialog will
 * use to filter the files it shows to the argument,
 * which may be null.
 *
 * @param extensions the file extension filter
 */
public void setFilterExtensions (char[][] extensions) {
	filterExtensions = extensions;
}

/**
 * Sets the file names which the dialog will
 * use to filter the files it shows to the argument,
 * which may be null.
 *
 * @param names the file name filter
 */
public void setFilterNames (char[][] names) {
	filterNames = names;
}

/**
 * Sets the directory path that the dialog will use
 * to the argument, which may be null. File names in this
 * path will appear in the dialog, filtered according
 * to the filter extensions.
 *
 * @param string the directory path
 * 
 * @see #setFilterExtensions
 */
public void setFilterPath (char[] string) {
	filterPath = string;
}

}
